<template>
  <canvas ref="canvasRef" class="scene-canvas" />
</template>

<script setup lang="ts">
import * as THREE from 'three'
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
import { threeTool, LightClass } from '@/utils/threeClass'
const canvasRef = ref()
const web3Dtool = new threeTool()
const lightTool = new LightClass()
import img3 from '@/assets/img/brick_roughness.jpg'
import disc from '@/assets/img/disc.png'

function BufferGeometryFun(){
  //创建空几何体
  const geometry = new THREE.BufferGeometry()

  //定义顶点坐标（3个点构成三角形
  const vertices = new Float32Array([
    -1,1,0,
    -1,-1,0,
    1,-1,0,
    1,1,0
  ])

  // 3. 为几何体添加"位置属性"
  // 第二个参数3表示每个顶点有3个分量（x,y,z）
  geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))

  // 4. 可选：添加颜色属性（每个顶点一个颜色，rgba）
  const colors = new Float32Array([
    1, 0, 0, 1,   // 顶点0：红色
    0, 1, 0, 1,   // 顶点1：绿色
    0, 0, 1, 1    // 顶点2：蓝色
  ]);
  // 第二个参数4表示每个颜色有4个分量（r,g,b,a）
  geometry.setAttribute('color', new THREE.BufferAttribute(colors, 4));

  // 索引数组：定义两个三角形的顶点顺序（0-1-2 和 0-2-3）
  // 每个数值对应positions数组中的顶点索引
  const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
  geometry.setIndex(new THREE.BufferAttribute(indices, 1));

  // // 计算法线（光照必需）
  // geometry.computeVertexNormals();

  //创建材质
  const material = new THREE.MeshBasicMaterial({
    vertexColors: true, // 启用顶点颜色
    side: THREE.DoubleSide  // 显示两面
  })

  // 生成网格
  const triangle = new THREE.Mesh(geometry, material);
  web3Dtool.scene.add(triangle)



  // UV坐标（纹理映射）
  const uvs = new Float32Array([
    0, 0,  // 顶点1对应纹理左下
    1, 0,  // 顶点2对应纹理右下
    1, 1, // 顶点3对应纹理顶部
    0, 1
  ]);

  geometry.setAttribute(
    'uv',
    new THREE.BufferAttribute(uvs, 2) // 2个数值表示一组UV
  );

  // // 应用纹理
  const textureLoader = new THREE.TextureLoader();
  material.map = textureLoader.load(img3);

  // const DirectionaLight = lightTool.createDirectionalLight()
  // scene.add(DirectionaLight)
}
/**
 * @name 简单抛物面
 * 球面：用球坐标（半径、方位角、极角）计算顶点，再用三角形拼接成球面。
 * 圆柱面：绕 Y 轴排列顶点，形成侧面 + 上下底面。
 * 抛物面 / 双曲面：通过二次方程计算 Z 轴坐标（如 z = x² + y²）。
 */
function reactifyObject() {
  const geometry = new THREE.BufferGeometry()
  const size = 1; // 控制抛物面大小
  const segments = 20; // 控制抛物面精细程度
  const positions = new Float32Array((segments + 1) * (segments + 1) * 3);

  let index = 0;
  for (let i = 0; i <= segments; i++) {
    for (let j = 0; j <= segments; j++) {
      //计算x、y坐标(范围：-size/2 ~ size/2)
      const x = (j /segments) * size - (size / 2);
      const y = (i /segments) * size - (size / 2);

      //计算z坐标(抛物线方程：z = x² + y²)
      const z = x * x + y * y;
      positions[index] = x;
      positions[index + 1] = y;
      positions[index + 2] = z;

      index += 3;
    }
  }
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

  // 计算索引（将网格分割为三角行）
  const indices = []
  for (let i = 0; i < segments; i++) {
    for (let j = 0; j < segments; j++) {
      const a = i * (segments + 1) + j;
      const b = (i + 1) * (segments + 1) + j;
      const c = (i + 1) * (segments + 1) + (j + 1);
      const d = i * (segments + 1) + (j + 1);

      indices.push(a, b, c, a, c, d);
    }
  }
  geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indices), 1));
  geometry.computeVertexNormals(); // 计算法线（光照必需）

  const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
  const mesh = new THREE.Mesh(geometry, material);
  web3Dtool.scene.add(mesh);
}

/**
 * @name 粒子系统
 * 通过海量顶点（数千至数百万）创建粒子效果（如星空、烟雾、雨滴）
 */
function particleSystem() {
  const count = 10000; // 粒子数量
  const geometry = new THREE.BufferGeometry();
  const positions = new Float32Array(count * 3); // 3个数值表示一个顶点（x,y,z）

  //Float32Array使用归一化的 0.0-1.0 浮点数，两者可以通过 value / 255 相互转换
  const colors = new Float32Array(count * 3); // 3个数值表示一个顶点（r,g,b）
  //Uint8Array的颜色分量范围是 0 到 255（整数）
  // const colors = new Uint8Array(count * 3); // 3个数值表示一个顶点（r,g,b）

  const sprite = new THREE.TextureLoader().load( disc );
  sprite.colorSpace = THREE.SRGBColorSpace;
  for (let i = 0; i < count * 3; i++) {
    positions[i] = (Math.random() - 0.5) * 10; // 随机位置
    colors[i] = Math.random(); // 随机颜色Float32Array类型
    // colors[i] = Math.random() * 255; // 随机颜色
  }
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
  // geometry.setAttribute('color', new THREE.Uint8BufferAttribute(colors, 3, true));
  geometry.computeBoundingSphere();
  //粒子材质

  const canvasPoint = document.createElement( 'canvas' );
  canvasPoint.width = 128;
  canvasPoint.height = 128;
  const context = canvasPoint.getContext( '2d' ) as CanvasRenderingContext2D;
  context.arc( 64, 64, 64, 0, 2 * Math.PI );
  context.fillStyle = '#fff';
  context.fill();
  const texture = new THREE.CanvasTexture( canvasPoint );
  texture.colorSpace = THREE.SRGBColorSpace;
  const material = new THREE.PointsMaterial({ vertexColors: true, map: texture, transparent: true, alphaTest: 0.1, size: 0.05, });// 透明度设置transparent: true, opacity: 0.8
  const particles = new THREE.Points(geometry, material);
  web3Dtool.scene.add(particles);

  function animate() {
    const positionsArray = geometry.attributes.position.array;
    requestAnimationFrame(animate);
    const time = Date.now() * 0.001;
    for (let i = 0; i < positionsArray.length; i++) {
      const x = positionsArray[i];
      const y = positionsArray[i + 1];
      //波动公式：在抛物面基础上叠加正弦波
      positionsArray[i + 2] = Math.sin(3 * x + time * 2);
    }
    geometry.attributes.position.needsUpdate = true;
  }
  // animate();
}
/**
 * @name 用粒子创建类似水面的波纹效果
 */
function particleSystemWater() {
  // 顶点着色器
  const vertexShader = `
    attribute float size;
    attribute vec3 customColor;

    varying vec3 vColor;

    void main() {
      vColor = customColor;

      vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
      gl_PointSize = size * (300.0 / -mvPosition.z); // 透视缩放

      gl_Position = projectionMatrix * mvPosition;
    }
  `;

  // 片元着色器
  const fragmentShader = `
    varying vec3 vColor;
    uniform sampler2D pointTexture;

    void main() {
      gl_FragColor = vec4(vColor, 1.0);
      gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord);

      // 圆形粒子
      if (length(gl_PointCoord - vec2(0.5)) > 0.5) {
        discard;
      }
    }
  `;

  const geometry = new THREE.BufferGeometry();
  const width = 100;
  const length = 100;
  const numPoints = width * length;
  const positions = new Float32Array(numPoints * 3);
  const colors = new Float32Array(numPoints * 3);
  let k = 0;
  for (let i = 0; i < width; i++) {
    for (let j = 0; j < length; j++) {
      const u = i / width;
      const v = j / length;
      const x = u - 0.5;
      const y = (Math.cos(u * Math.PI * 8) + Math.sin(v * Math.PI * 8)) * 0.05;
      const z = v - 0.5;

      positions[3 * k] = x * 10;
      positions[3 * k + 1] = y * 5;
      positions[3 * k + 2] = z * 10;
      colors[3 * k] = u;
      colors[3 * k + 1] = 1;
      colors[3 * k + 2] = v;
      k++;
    }
  }
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
  geometry.computeBoundingBox();

  const texture = new THREE.TextureLoader().load( disc );
  texture.colorSpace = THREE.SRGBColorSpace;
  const material = new THREE.PointsMaterial({ vertexColors: true, map: texture, transparent: true, alphaTest: 0.1, size: 0.05, });// 透明度设置transparent: true, opacity: 0.8
  const particles = new THREE.Points(geometry, material);
  web3Dtool.scene.add(particles);


  function animate(){
    requestAnimationFrame(animate);
    const positioinsArray = geometry.attributes.position.array;
    const time = Date.now() * 0.001;
    for (let i = 0; i < positioinsArray.length; i += 3) {
      const x = positioinsArray[i];
      const y = positioinsArray[i + 1]
      const z = positioinsArray[i + 2];
      positioinsArray[i + 1] = (Math.sin( Math.PI * x + time ) + Math.cos( Math.PI * z + time )) * 0.1;
    }
    geometry.attributes.position.needsUpdate = true;
  }
  animate();
}

/**
 * @name 雨雪特效
 */
function rainSnow() {
  const rainCount = 5000;
  const rainGeometry = new THREE.BufferGeometry();
  const rainPositions = new Float32Array(rainCount * 3);
  for (let i = 0; i < rainCount; i++) {
    rainPositions[i] = Math.random() * 100 - 50; // x
    rainPositions[i+1] = Math.random() * 50 + 50; // y
    rainPositions[i+2] = Math.random() * 100 - 50; // z
  }

  rainGeometry.setAttribute('position', new THREE.BufferAttribute(rainPositions, 3));

  const rainMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.1 });
  const rainPoints = new THREE.Points(rainGeometry, rainMaterial);
  web3Dtool.scene.add(rainPoints);

  function animate() {
    const positionsArray = rainGeometry.attributes.position.array;
    requestAnimationFrame(animate);

    for( let i = 0; i < positionsArray.length; i += 3) {
      positionsArray[i + 1] -= 0.5;
      if(positionsArray[i + 1] < -50){
        positionsArray[i] = Math.random() * 100 - 50;
        positionsArray[i+1] = Math.random() * 50 + 50; // 重置Z
        positionsArray[i+2] = Math.random() * 100 - 50; // 重置X
      }
    }
    rainGeometry.attributes.position.needsUpdate = true;
  }
  animate();
}
onMounted(() => {
  web3Dtool.setCanvasMain({ bgColor: 0x000000, canvasEl: canvasRef.value })
  web3Dtool.initScene()
  web3Dtool.animateFun()

  // BufferGeometryFun()
  // reactifyObject()
  // particleSystem()
  //水波纹
  // particleSystemWater()
  //雨雪特效
  rainSnow()

})

// 5. 监听窗口大小变化
watch([width, height], e => {
  web3Dtool.watchUpdate(e[0], e[1])
})
</script>

<style scoped lang="scss">
.scene-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  outline: none;
}
</style>
